import threading
import json
import os
from datetime import datetime, timedelta
import shutil

# 自定义JSON编码器，处理datetime对象
class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super(DateTimeEncoder, self).default(obj)

# 文件缓存实现
class CacheManager:
    """线程安全的文件缓存管理器，管理微信登录相关缓存"""
    
    def __init__(self, cache_dir="cache"):
        # 创建缓存目录（相对于utils目录）
        self.cache_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", cache_dir)
        self._lock = threading.RLock()
        self._cleanup_interval = 3600  # 每小时清理一次过期缓存
        self._last_cleanup = datetime.now()
        
        # 确保缓存目录存在
        os.makedirs(self.cache_dir, exist_ok=True)
    
    def _get_cache_file_path(self, key):
        """
        获取缓存文件路径
        
        Args:
            key: 缓存键
            
        Returns:
            文件路径
        """
        # 生成安全的文件名，替换Windows不支持的字符
        safe_filename = key.replace("/", "_")
        safe_filename = safe_filename.replace(":", "_")
        return os.path.join(self.cache_dir, f"{safe_filename}.json")
    
    def _cleanup_expired(self):
        """清理过期的缓存文件"""
        current_time = datetime.now()
        if (current_time - self._last_cleanup).total_seconds() > self._cleanup_interval:
            with self._lock:
                try:
                    for filename in os.listdir(self.cache_dir):
                        if filename.endswith('.json'):
                            file_path = os.path.join(self.cache_dir, filename)
                            try:
                                with open(file_path, 'r', encoding='utf-8') as f:
                                    cache_item = json.load(f)
                                
                                # 检查是否过期
                                if cache_item.get('expire_time'):
                                    expire_time = datetime.fromisoformat(cache_item['expire_time'])
                                    if current_time > expire_time:
                                        os.remove(file_path)
                            except Exception:
                                # 如果文件损坏或无法读取，删除它
                                try:
                                    os.remove(file_path)
                                except:
                                    pass
                except Exception:
                    pass  # 忽略目录访问错误
                
                self._last_cleanup = current_time
    
    def set(self, key, value, expire_seconds=None):
        """设置缓存项到文件
        
        Args:
            key: 缓存键
            value: 缓存值
            expire_seconds: 过期时间（秒），None表示永不过期
        """
        with self._lock:
            file_path = self._get_cache_file_path(key)
            
            # 准备缓存数据
            cache_item = {
                'value': value,
                'created_at': datetime.now().isoformat()
            }
            
            if expire_seconds is not None:
                expire_time = datetime.now() + timedelta(seconds=expire_seconds)
                cache_item['expire_time'] = expire_time.isoformat()
            
            # 写入临时文件，然后原子重命名以避免并发写入问题
            temp_file = file_path + '.tmp'
            try:
                with open(temp_file, 'w', encoding='utf-8') as f:
                    json.dump(cache_item, f, ensure_ascii=False, indent=2, cls=DateTimeEncoder)
                
                # 原子重命名（在大多数系统上是原子的）
                os.replace(temp_file, file_path)
            except Exception as e:
                # 清理临时文件
                if os.path.exists(temp_file):
                    try:
                        os.remove(temp_file)
                    except:
                        pass
                print(f"缓存写入失败: {e}")
    
    def get(self, key):
        """从文件获取缓存项，如果过期则返回None
        
        Args:
            key: 缓存键
            
        Returns:
            缓存值或None
        """
        # 先清理过期项
        self._cleanup_expired()
        
        file_path = self._get_cache_file_path(key)
        if not os.path.exists(file_path):
            return None
        
        with self._lock:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    cache_item = json.load(f)
                
                # 检查是否过期
                if cache_item.get('expire_time'):
                    expire_time = datetime.fromisoformat(cache_item['expire_time'])
                    if datetime.now() > expire_time:
                        try:
                            os.remove(file_path)
                        except:
                            pass
                        return None
                
                return cache_item['value']
            except Exception:
                # 如果文件损坏，删除它
                try:
                    os.remove(file_path)
                except:
                    pass
                return None
    
    def delete(self, key):
        """删除缓存项文件
        
        Args:
            key: 缓存键
        """
        file_path = self._get_cache_file_path(key)
        if os.path.exists(file_path):
            with self._lock:
                try:
                    os.remove(file_path)
                except:
                    pass
    
    def clear(self):
        """清空所有缓存文件"""
        with self._lock:
            try:
                shutil.rmtree(self.cache_dir)
                os.makedirs(self.cache_dir, exist_ok=True)
            except Exception:
                pass  # 忽略目录操作错误
    
    def exists(self, key):
        """检查缓存键是否存在
        
        Args:
            key: 缓存键
            
        Returns:
            是否存在
        """
        return self.get(key) is not None
    
    def get_cache_info(self):
        """获取缓存信息（用于调试）
        :return: 缓存统计信息
        """
        self._cleanup_expired()
        total = 0
        valid_items = 0
        
        try:
            for filename in os.listdir(self.cache_dir):
                if filename.endswith('.json'):
                    total += 1
                    # 检查文件是否有效（未过期）
                    key = filename[:-5].replace("_", "/")
                    if self.get(key) is not None:
                        valid_items += 1
        except Exception:
            pass  # 忽略目录访问错误
          
        return {
            'total_items': total,
            'valid_items': valid_items,
            'expired_items': total - valid_items
        }
        
    def get_all_keys(self):
        """获取所有未过期的缓存键
        
        Returns:
            缓存键列表
        """
        self._cleanup_expired()
        keys = []
        
        try:
            for filename in os.listdir(self.cache_dir):
                if filename.endswith('.json'):
                    # 移除.json后缀和处理过的特殊字符
                    key = filename[:-5].replace("_", "/")
                    # 再次检查是否真的有效（未过期）
                    if self.get(key) is not None:
                        keys.append(key)
        except Exception:
            pass  # 忽略目录访问错误
        
        return keys

# 创建全局缓存实例
cache_manager = CacheManager()

# 微信会话相关的缓存键生成函数
def generate_wechat_cache_key(code):
    """
    生成微信code对应的缓存键
    :param code: 微信登录code
    :return: 缓存键
    """
    return f"wechat:code:{code}"

def generate_openid_cache_key(openid):
    """
    生成openid对应的缓存键
    :param openid: 微信用户openid
    :return: 缓存键
    """
    return f"wechat:openid:{openid}"